package com.example.udpscreen.runnables;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;

import com.example.udpscreen.SyncLock;
import com.example.udpscreen.enums.UdpSocketDataType;
import com.example.udpscreen.listeners.FinishListener;
import com.example.udpscreen.listeners.ProgressListener;
import com.example.udpscreen.models.ResumeFileRecord;
import com.example.udpscreen.servers.BaseSocket;
import com.example.udpscreen.utils.Convert;
import com.example.udpscreen.utils.DataFormat;
import com.example.udpscreen.utils.FileUtil;

public class RecvResumeFileRunnable implements Runnable {

	private final static byte CMD_RECEIPT = 0x11, CMD_PART_END = 0X10, CMD_DATA = 0x0A;

	private SyncLock lock = new SyncLock();

	private BaseSocket mSocket;

	private InetAddress mRemoteAddress;
	private int mRemotePort;

	private String mFilePath;

	private String mFileName;

	private String mMd5;

	private long mFileSize;
	
	private static  int PACKAGE_COUNT = 30,PACKAGE_LEN =1024 * 5;
	
	private boolean mIsActive = true;

	private ArrayList<byte[]> recvFileArrayList;
	private int[] mReciveMapping;

	private ProgressListener mProgressListener;

	private FinishListener mFinishListener;
	
	public boolean getRunStatus(){
		return mIsActive;
	}

	public RecvResumeFileRunnable(BaseSocket socket, ProgressListener progresslistener, FinishListener finishlistener) {
		this.mSocket = socket;
		this.mProgressListener = progresslistener;
		this.mFinishListener = finishlistener;
	}

	public void updateData(String file_path, String file_name, long file_size, String md5, InetAddress address,
			int port) {
		this.mFilePath = file_path;
		this.mMd5 = md5;
		this.mFileName = file_name;
		this.mFileSize = file_size;
		this.mRemoteAddress = address;
		this.mRemotePort = port;
	}

	@Override
	public void run() {
		long total = 0;
		boolean issuccess = false;
		String error_content = "";
		String file_path = FileUtil.ResumeFilePath + File.separator + mFileName;
		File file = new File(file_path);
		List<ResumeFileRecord> records = new ArrayList<ResumeFileRecord>();
		boolean isrecved = false;
		if (file.exists()) {
			try {
				records = getResumeFileRecord();
			} catch (JSONException e) {
				e.printStackTrace();
			}
			if (records != null) {
				isrecved = checkContains(this.mFileName, this.mMd5, records);
			}
		}
		if (!isrecved) {
			saveResumeFileRecord(records);
		}

		RandomAccessFile access = null;
		long begin = System.currentTimeMillis();
		try {
			access = new RandomAccessFile(file, "rw");
			long recved_size = 0;
			if (isrecved)
				recved_size = file.length();
			recvFileArrayList = new ArrayList<byte[]>();
			
			// 发送给控制端 已接收的大小
			sendToController(recved_size);

			total = mFileSize - recved_size;
			access.seek(recved_size);

			while (total > 0 && mIsActive) {
				
				
				int size = PACKAGE_LEN * PACKAGE_COUNT;

				if (total < size) {
					// 在当前文件基础上写满剩余的字节
					size = (int) total;
					if (size % (PACKAGE_LEN) > 0) {
						PACKAGE_COUNT = size / (PACKAGE_LEN) + 1;
					} else
						PACKAGE_COUNT = size / (PACKAGE_LEN);
				}
				mReciveMapping = new int[PACKAGE_COUNT];
				for (int arraySize = 0; arraySize < PACKAGE_COUNT; arraySize++) {
					recvFileArrayList.add(arraySize, new byte[0]);
				}
				
				do {
					if(lock.Wait(12000)){
						throw new RuntimeException("等待超时.. 退出接收线程");
					}

					// 等待接收到PartEnd 进行统计和索要序号

					if (!calcLostNumber(access)) {
						break;
					}
				} while (mIsActive);// 当满足条件后结束循环

				recved_size += size;

				total = total - size;

				if (mProgressListener != null)
					mProgressListener.progessChanged(recved_size, mFileSize);
			}
			if(mIsActive){
				issuccess = true;
				error_content="success";
			}else{
				issuccess = false;
				error_content="用户主动退出了接收线程";
			}
			
			long end  = System.currentTimeMillis();
			System.out.println("total  millis:"+ (end-begin));

		} catch (Exception e) {
			e.printStackTrace();
			issuccess = false;
			error_content="error:"+e.getMessage();
		} finally {
			
		}

		if (mFinishListener != null)
			mFinishListener.finish(issuccess,error_content);

	}

	private void sendToController(long recv_filesize) throws JSONException, IOException {
		JSONObject json = new JSONObject();
		json.put("cmd", 2);
		json.put("file_name", mFileName);
		json.put("file_path", mFilePath);
		json.put("file_md5", mMd5);
		json.put("recived_file_size", recv_filesize);

		byte[] sendbuffer = DataFormat.formatCommand(json.toString());

		DatagramPacket packet = new DatagramPacket(sendbuffer, sendbuffer.length, mRemoteAddress, mRemotePort);
		mSocket.sendDatagramPacket(packet);
		System.out.println(" send  recvied file size :" + recv_filesize);
	}

	/**
	 * 保存断点续传文件记录
	 * 
	 * @param records
	 */
	private void saveResumeFileRecord(List<ResumeFileRecord> records) {

		Iterator<ResumeFileRecord> iterator = records.iterator();
		while (iterator.hasNext()) {
			ResumeFileRecord record = iterator.next();
			if (record.FileName.equals(mFileName)) {
				iterator.remove();
			}
		}
		ResumeFileRecord resumefile = new ResumeFileRecord(mFileName, mMd5);
		records.add(resumefile);

		try {
			String json_str = ResumeFileRecord.structureFileText(records);
			FileUtil.writeFile(FileUtil.ResumeFileRecordsFilePath, json_str);
		} catch (JSONException e) {
			e.printStackTrace();
		}
	}

	private List<ResumeFileRecord> getResumeFileRecord() throws JSONException {
		// 读取记录文件
		String jsonarray_str = FileUtil.readFile(FileUtil.ResumeFileRecordsFilePath);

			if (null != jsonarray_str && !"".equals(jsonarray_str)) {
			// 读取列表
			List<ResumeFileRecord> records = ResumeFileRecord.analysisTextFile(jsonarray_str);
			return records;
		}
		return null;
	}

	/**
	 * @param file_name
	 * @param md5
	 * @return
	 */
	private boolean checkContains(String file_name, String md5, List<ResumeFileRecord> records) {
		boolean result = false;

		for (int i = 0; i < records.size(); i++) {
			ResumeFileRecord record = records.get(i);
			if (file_name.equals(record.FileName)) {
				// 判断 MD5值是否一致
				if (md5.equals(record.MD5)) {
					result = true;
					break;
				} else {
					records.remove(record);
					break;
				}
			}
		}

		return result;
	}

	private boolean calcLostNumber(RandomAccessFile access) {
		List<Integer> list = new ArrayList<Integer>();
		for (int i = 0; i < mReciveMapping.length; i++) {
			if (mReciveMapping[i] == 0)
				list.add(i);
		}
		boolean need_resend = list.size() > 0 ? true : false;
		JSONObject json_object = new JSONObject();
		try {

			if (!need_resend) {// 写入文件
				for (int re = 0; re < recvFileArrayList.size(); re++) {// 循环写入文件
					byte[] buffs = recvFileArrayList.get(re);
					access.write(buffs, 0, buffs.length);
				}
				recvFileArrayList.clear();// 清理缓存数组
				// 防止下面接受循环时 数组越界
			}

			json_object.put("need_resend", need_resend);

			JSONArray json_array = new JSONArray(list);
			json_object.put("numbers", json_array);

			String json_str = json_object.toString();
			byte[] json_bytes = json_str.getBytes();
			byte[] send_buffer = new byte[json_bytes.length + 10];
			send_buffer[0] = UdpSocketDataType.FILE_RESUME_SEND;
			send_buffer[1] = (byte) (255 - UdpSocketDataType.FILE_RESUME_SEND);
			send_buffer[2] = CMD_RECEIPT;
			System.arraycopy(json_bytes, 0, send_buffer, 10, json_bytes.length);

			DatagramPacket packet = new DatagramPacket(send_buffer, send_buffer.length, mRemoteAddress, mRemotePort);
			mSocket.sendDatagramPacket(packet);
			System.out.println("calc lost packet count: " + list.size());
		} catch (JSONException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return need_resend;

	}

	public void whriteData(byte[] data, int real_len) {
		int type = data[2];
		if (type == CMD_DATA) {
			byte[] number_bytes = new byte[2];
			System.arraycopy(data, 3, number_bytes, 0, number_bytes.length);
			int number = Convert.bytes2UnsignedShort(number_bytes);
			mReciveMapping[number] = 1;
			// 写文件
			byte[] recevFileBytes = new byte[real_len-10];
			System.arraycopy(data, 10, recevFileBytes, 0, recevFileBytes.length);
			recvFileArrayList.add(number, recevFileBytes);
		} else if (type == CMD_PART_END) {
			lock.Notify();
			System.out.println("recv part end!");
		}

	}
	
	
	public void onDestory(){
		mIsActive = false;
		lock.Notify();
	}

}
